// Global variables

var gBundle = Components.classes["@mozilla.org/intl/stringbundle;1"].getService(Components.interfaces.nsIStringBundleService);
var bugmenotstrings = gBundle.createBundle("chrome://bugmenot/locale/bugmenot.properties");
var unablelocateformalert = bugmenotstrings.GetStringFromName("unablelocateform");
var noaccountsfoundalert = bugmenotstrings.GetStringFromName("noaccountsfound");
var noneaccountsworkedconfirm = bugmenotstrings.GetStringFromName("noneaccountsworked");
var unablelocateloginalert = bugmenotstrings.GetStringFromName("unablelocatelogin");
var erroroccurredalert = bugmenotstrings.GetStringFromName("erroroccurred");
var httpRequest;
var httpRequestCallbackFunction;
var loginFormUrl;
var loginForm = false;
var bugSite;
var usernameElement = false;
var otherElement = false;
var passwordElement = false;
var submitElement = false;
var accountList;
var accountTry;
var loginResponseReceived = false;
 
// Options object

var bugmenotOptions = new Object();

// Set event to enable/disable BugMeNot menu items

window.addEventListener("load", _setToggleMenuItemEvent, true);

// BugMeNot Primary Menu Item

function BugMeNot() {
	// Execution starts here, this function is specified by bugmenotOverlay.xul
	loginFormUrl = window._content.document.location.href;
	bugSite = _currentDomain(); 
	if (_locateForm()) {
		_setOptions();
		// Get Username and Password from BugMeNot
		_httpGet('http://www.bugmenot.com/view/' + bugSite + '?utm_source=extension&utm_medium=firefox', _processBugMeNotResponse);
		// Processing continues in _processBugMeNotResponse
		// when http response from BugMeNot server is Received.
	} else {
		alert(unablelocateformalert);
	}
}

// BugMeNot Extra Menu Items

function BugMeNot_PopulateAccountsMenu() {
	var menu = document.getElementById("bugmenotaccounts-popup");
	for(var i=menu.childNodes.length - 1; i>=0; i--) {
		menu.removeChild(menu.childNodes.item(i));
	}

	for(var i=0; i < accountList.snapshotLength; i++)
	{
		var tempItem = null;
		tempItem = document.createElement("menuitem");
		var info = _accountTableInfo(i, /username|stats/);
		tempItem.setAttribute("id", "bugmentnotaccount-" + i);
		tempItem.setAttribute("label", info.username);
		tempItem.setAttribute("oncommand",  "BugMeNot_TryAccount(" + i + ")");
		tempItem.setAttribute("tooltiptext", info.stats);
		menu.appendChild(tempItem);
	}
}

function BugMeNot_TryAccount(accountNumber) {
	accountTry = accountNumber;
	_loginFromMenuItem();
}

function BugMeNot_Retry() {
	accountTry++;
	_loginFromMenuItem();
}

function BugMeNot_Success() {
	bugmenotOptions.showExtraMenuItems = false
	_castVote('Y');
}

function BugMeNot_Failure() {
	_castVote('N');
}


// event callbacks

function _processBugMeNotResponse () {
	// use responseText and parse with DOMParser
	// as bugmenot.com sends content as text/html 
	// so responseXml is null	
	var loginFormFound = true;
	var xmlString = "<?xml version=\"1.0\"?>\n" + httpRequest.responseText;
	var domParser = new DOMParser();
	var response = domParser.parseFromString(xmlString, 'text/xml');
	var xpe = new XPathEvaluator();
	accountList = xpe.evaluate('//*[@class="account"]|//*[@class="account valid"]', response, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
	if (accountList.snapshotLength > 0) {
		bugmenotOptions.showExtraMenuItems = true;
		accountTry = 0;
		_login();
		// Processing continues in _processLoginResponse
		// when http response from login attempt is Received.
	} else {
		alert(noaccountsfoundalert);
	}
}

function _processLoginResponse () {
	window.removeEventListener("load", _processLoginResponse, true);
	if (!bugmenotOptions.submitPage) {
		bugmenotOptions.submitPage = true;
		bugmenotOptions.submitPageDomain = _currentDomain();
	}
	if (_locateForm() && !loginResponseReceived) {
		loginResponseReceived = true;
		if (!bugmenotOptions.disableReportFailure) {
			_castVote('N');
		}
		if (!bugmenotOptions.disableAutoNext) {
			accountTry++;
			_loginRetry();
			// Processing continues in _processLoginResponse
			// when http response from login attempt is Received.
		}
	}
}

function _processLoginRetryRequest() {
	window.removeEventListener("load", _processLoginRetryRequest, true);
	 _loginRetryRequest() ;
}

// Local functions

function _locateForm () {
	var loginFormFound = false;
	if (bugmenotOptions.haveForm) {
		loginForm = bugmenotOptions.targetForm;
		loginFormFound = _isLoginForm(loginForm);
	}
	if (!loginFormFound) {
		var currentWindow = window._content;
		if (!currentWindow.document.forms.length > 0 && currentWindow.frames.length > 0) {
			for(var i = 0; i < currentWindow.frames.length; i++)	{
				loginFormFound = _locateFormInDocument(currentWindow.frames[i].document);
				if (loginFormFound) {
					break;
				}
			}
		} else {
			loginFormFound = _locateFormInDocument(currentWindow.document);
		}
		
	}
	return loginFormFound;
}

function _locateFormInDocument(targetDocument) {
	var loginFormFound = false;
	for (var i = 0; i < targetDocument.forms.length; i++) {
		loginForm = targetDocument.forms[i];
		loginFormFound = _isLoginForm(loginForm);
		if (loginFormFound) {
			break;
		}
	}
	return loginFormFound;
}

function _isLoginForm () {
	var loginFormFound = false;
	var usernameFound = false;
	var passwordFound = false;
	var submitFound = false;
	var anySubmitFound = false;
	usernameElement = false;
	otherElement = false;
	passwordElement = false;
	submitElement = false;
	for (var ii = 0; ii < loginForm.elements.length; ii++) {
		currentElement = loginForm.elements[ii];
		if (currentElement.type.match(/text/i) && currentElement.name.match(/ID|un|name|user|usr|log|email|mail|nick/i)) {
			if (usernameFound) {
				otherElement = usernameElement;
				usernameElement = currentElement;
			} else {
				usernameFound = true;
				usernameElement = currentElement;
			}
		}
		if (currentElement.type.match(/pass/i)) {
			passwordFound = true;
			passwordElement = currentElement;
		}
		if (currentElement.type.match(/submit/i) && !anySubmitFound) {
			submitElement = currentElement;
			anySubmitFound = true;
		}
		if (currentElement.type.match(/submit|button|image/i) &&  currentElement.value.match(/log|sub|sign/i)) {
			submitFound = true;
			submitElement = currentElement;
		}
		if (usernameFound && passwordFound && submitFound) {
			break;
		}
	}
	if (usernameFound && passwordFound) {
		loginFormFound = true;
	}
	return loginFormFound;
}

function _login() {
	window.addEventListener("load", _processLoginResponse, true);
	loginResponseReceived = false;
	var accountInfo = _accountTableInfo(accountTry, /username|password/);
	if (!otherElement) {
		usernameElement.value  = accountInfo.username;
	} else {
		accountParts = accountInfo.username.split('/');
		if (accountParts.length > 1) {
			otherElement.value  = accountParts[0].trim();
			usernameElement.value = accountParts[1].trim();
		} else {
			otherElement.value  = accountInfo.username;
			usernameElement.value =  accountInfo.username;
		}
	}
	passwordElement.value = accountInfo.password;
	if (!bugmenotOptions.disableAutoSubmit) {
		if (!submitElement) {
			loginForm.submit();
		} else {
			submitElement.click();
		}
	}
}

function _loginRetry() {
	if (accountTry < accountList.snapshotLength) {
		_login()
		// Processing continues in _processLoginResponse
		// when http response from login attempt is Received.
	} else {	
		if (confirm(noneaccountsworkedconfirm)){
			BugMeNot();
		}
	}
}

function _loginRetryRequest() {
	if (_locateForm()) {
		_loginRetry();
	} else {
		alert(unablelocateloginalert);
	}
}

function _loginFromMenuItem() {
	if (window._content.document.location != loginFormUrl && !_locateForm()) {
		window.addEventListener("load", _processLoginRetryRequest, true);
		window._content.document.location = loginFormUrl;
	} else {
		_loginRetryRequest();
	}
}

function _castVote(vote) {
	var voteUrl = 'http://www.bugmenot.com/vote_ajax.php?';
	var queryData = new Array();
	var info = _accountInfo(accountTry, /id|site/);
	for (var i in info) {
		queryData.push(i + '=' + info[i]);
	}
	queryData.push('vote=' + vote);
	_httpGet(voteUrl + queryData.join('&'), null);	
}

function _accountInfo(accountNumber, matchExpression) {
	var element = accountList.snapshotItem(accountNumber);
	var childElementList = element.getElementsByTagName('input');
	var accountInfo = new Object();
	for (var i = 0; i < childElementList.length; i++) {
		if (childElementList[i].name.match(matchExpression)) {
			accountInfo[childElementList[i].name] = childElementList[i].value;
		}
	}
	return accountInfo;
}

function _accountTableInfo(accountNumber, matchExpression) {
	var element = accountList.snapshotItem(accountNumber);
	var childElementList = element.getElementsByTagName('tr');
	var accountInfo = new Object();
	for (var i = 0; i < childElementList.length; i++) {
		var rowHeading = childElementList[i].getElementsByTagName('th')[0].textContent.trim().toLowerCase();
		if (rowHeading.match(matchExpression)) {	
			accountInfo[rowHeading] = childElementList[i].getElementsByTagName('td')[0].textContent.trim();
		}
	}
	return accountInfo;
}

function _setOptions() {
	var bugmenotPrefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch("bugmenot.");
	if (bugmenotPrefs.prefHasUserValue("optDisableAutoSubmit")) {
		bugmenotOptions.disableAutoSubmit = bugmenotPrefs.getBoolPref("optDisableAutoSubmit");
	} else {
		bugmenotOptions.disableAutoSubmit = false;
	}
	if (bugmenotPrefs.prefHasUserValue("optDisableAutoNext")) {
		bugmenotOptions.disableAutoNext = bugmenotPrefs.getBoolPref("optDisableAutoNext");
	} else {
		bugmenotOptions.disableAutoNext = false;
	}
	if (bugmenotPrefs.prefHasUserValue("optDisableReportFailure")) {
		bugmenotOptions.disableReportFailure = bugmenotPrefs.getBoolPref("optDisableReportFailure");
	} else {
		bugmenotOptions.disableReportFailure = false;
	}
	if (bugmenotPrefs.prefHasUserValue("optDisableExtraMenu")) {
		bugmenotOptions.disableExtraMenu = bugmenotPrefs.getBoolPref("optDisableExtraMenu");
	} else {
		bugmenotOptions.disableExtraMenu = false;
	}
	
	bugmenotOptions.showExtraMenuItems = false;
	bugmenotOptions.haveForm = false;
	bugmenotOptions.submitPage = false;
	bugmenotOptions.submitPageDomain = null;
}

function _setToggleMenuItemEvent() {
	window.removeEventListener("load", _setToggleMenuItemEvent, true);
	document.getElementById("contentAreaContextMenu").addEventListener("popupshowing", _toggleMenuItems, false);
	// Processing continues in _toggleMenuItems
	// when context menu is accessed.
}

function _toggleMenuItems() {
	if (gContextMenu.onTextInput) {
		bugmenotOptions.haveForm = true;
		bugmenotOptions.targetForm = gContextMenu.target.form;
	}
	document.getElementById("bugmenot").setAttribute('hidden', !gContextMenu.onTextInput);
	var hideSeparator = !gContextMenu.onTextInput;
	var currentDomain = _currentDomain();
	if (bugmenotOptions.submitPageDomain != currentDomain) {
		bugmenotOptions.showExtraMenuItems = false;
	}
	if (bugmenotOptions.showExtraMenuItems && !bugmenotOptions.disableExtraMenu) {
		hideSeparator = false;
		document.getElementById("bugmenotcurrent").setAttribute('label', bugSite);
		document.getElementById("bugmenotcurrent").setAttribute('hidden', false);
		document.getElementById("bugmenotsuccess").setAttribute('hidden', false);
		if (bugmenotOptions.disableReportFailure) {
			document.getElementById("bugmenotfailure").setAttribute('hidden', false);
		} else {
			document.getElementById("bugmenotfailure").setAttribute('hidden', true);
		}
		if (accountList.snapshotLength > 0) {
			document.getElementById("bugmenotaccounts").setAttribute('hidden', false);
		} else {
			document.getElementById("bugmenotaccounts").setAttribute('hidden', false);
		}
		if (accountList.snapshotLength > accountTry) {
			document.getElementById("bugmenotretry").setAttribute('hidden', false);
		} else {
			document.getElementById("bugmenotretry").setAttribute('hidden', true);
		}
	} else {
		document.getElementById("bugmenotcurrent").setAttribute('hidden', true);
		document.getElementById("bugmenotretry").setAttribute('hidden', true);
		document.getElementById("bugmenotfailure").setAttribute('hidden', true);
		document.getElementById("bugmenotsuccess").setAttribute('hidden', true);
		document.getElementById("bugmenotaccounts").setAttribute('hidden', true);
	}
	document.getElementById("bugmenotseparator").setAttribute('hidden', hideSeparator);
}

function _currentDomain() {
	var matchLocation = window._content.document.location.href.match(/\/\/([^\/]+)\//);
	var currentDomain = matchLocation[1];
	return currentDomain;
}

/* 
Dorian Scholz added the following snippet to the httpExecuteCallback function
inform the user that there are no accounts for the requested website in case of a 404.
And for all other response codes the user gets an error message with the response code.
*/

function _httpExecuteCallback() {
   if (httpRequestCallbackFunction != null) {
	   if (httpRequest.readyState == 4) {
		   if (httpRequest.status == 200) {
			   httpRequestCallbackFunction();
			   httpRequestCallbackFunction = null;
		   } else if (httpRequest.status == 404) {
			 alert(noaccountsfoundalert);
		   } else {
			 alert((erroroccurredalert) + httpRequest.status);
		   }
	   }
   }
}

function _httpGet(url, callbackFunction) {
	httpRequest = false;
	httpRequestCallbackFunction = callbackFunction;
	httpRequest = new XMLHttpRequest();
	httpRequest.onreadystatechange = _httpExecuteCallback;
	httpRequest.open('GET', url, true);
	httpRequest.send(null);
}

// String trim prototype method

String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ""); };

